#ifndef __D_MINIMIZE1D_H__
#define __D_MINIMIZE1D_H__

#include <malloc.h>
#include <math.h>
#include "common1d.h"
#include "common2d.h"
#include "minimize1dcl.h"

#define __D_CL1D__

const	size fetch_size1d	= FETCH_SIZE_1D;

inline void	calc_test1d(test1d& _test1d, test_func* _func, arg _arg, args _args)
{
	_test1d.coord	= _arg;
	_args[0]		= _arg;
	_test1d.value	= (*_func)(_args);
}

inline arg	calc_r1d(const test1d& _test_left, const test1d& _test_right, const arg& _m)
{
	return _m * (_test_right.coord - _test_left.coord) + 
		(_test_right.value - _test_left.value) * (_test_right.value - _test_left.value) /
		(_m * (_test_right.coord - _test_left.coord)) - 2 * (_test_right.value + _test_left.value);
}

inline void	calc_m1d(const test1d& _test_left, const test1d& _test_right, arg& _m)
{
	arg curr_m	= fabs((_test_right.value - _test_left.value) / (_test_right.coord - _test_left.coord));
	if (curr_m > _m)
	{
		_m	= curr_m;
	}
}

// ---
inline test1d	calc_test_and_m1d(test_func* _func, args _args, const test1d& _test_left, const test1d& _test_right, arg& _m)
{
	arg new_arg	= (_test_right.coord + _test_left.coord) / 2 - (_test_right.value - _test_left.value) / (2 * _m);
	if ((new_arg <= _test_left.coord) || (new_arg >= _test_right.coord))
	{
		new_arg	= (_test_right.coord + _test_left.coord) / 2;
	}

	test1d new_test;
	calc_test1d(new_test, _func, new_arg, _args);

	calc_m1d(_test_left, new_test, _m);
	calc_m1d(new_test, _test_right, _m);

	return new_test;
}
// ---
void	insert_test1d(test1d** _tests_array, size& _tests_size, size& _tests_size_max, 
					  size _index, test1d& _new_test)
{
	test1d* tests_array	= *_tests_array;
	if (_tests_size == _tests_size_max)
	{
		_tests_size_max		*= 2;
		test1d* new_array	= (test1d*)malloc(sizeof(test1d) * _tests_size_max);

		for (size i = 0; i < _index; ++i)
		{
			new_array[i]	= tests_array[i];
		}
		new_array[_index]	= _new_test;
		for (size i = _index; i < _tests_size; ++i)
		{
			new_array[i + 1]	= tests_array[i];
		}
		++_tests_size;
		*_tests_array	= new_array;
		free(tests_array);		
	}
	else
	{
		for (size i = _tests_size; i > _index; --i)
		{
			tests_array[i]	= tests_array[i - 1];
		}
		tests_array[_index]	= _new_test;
		++_tests_size;
	}
}

// ---
test1d	minimize1d(test_func* _func, args _args, size _argc, arg _min, arg _max, arg _eps)
{
	size tests_size_max	= fetch_size1d;
	size tests_size		= 2;
	test1d*	tests_array	= (test1d*)malloc((sizeof(test1d) * tests_size_max));

	test_func* func	= _func;
	args f_args		= _args;

	arg eps	= _eps;

	calc_test1d(tests_array[0], _func, _min, f_args);
	calc_test1d(tests_array[1], _func, _max, f_args);

	arg prev_min	= 0;
	arg curr_min	= ARG_MAX;
	arg curr_min_coord	= 0;

	arg m	= 1;
	arg t	= 1;

	do
	{
		arg r_max			= -ARG_MAX;
		size r_max_index	= 1;

		for (size i = 1; i < tests_size; ++i)
		{
			arg r_temp	= calc_r1d(tests_array[i - 1], tests_array[i], m);

			if (r_max < r_temp)
			{
				r_max		= r_temp;
				r_max_index	= i;
			}
		}

		curr_min_coord	= (tests_array[r_max_index].coord + tests_array[r_max_index - 1].coord) / 2;
		test1d new_test	= calc_test_and_m1d(func, f_args, tests_array[r_max_index - 1], tests_array[r_max_index], m);
		
		prev_min	= curr_min;
		curr_min	= new_test.value;

		insert_test1d(&tests_array, tests_size, tests_size_max, r_max_index, new_test);
	}
	while (fabs(curr_min - prev_min) > eps);

	test1d min_test;
	min_test.coord	= curr_min_coord;
	min_test.value	= curr_min;
	
	return min_test;
}

void	minimize_fetch1d(test_func* _func, test2d* _fetch_array, args _args, size _argc, arg _min, arg _max, arg _eps)
{
#ifndef __D_CL1D__
	for (size i = 0; i < fetch_size2d; ++i)
	{
		_args[1]	= _fetch_array[i].coord[1];
		test1d min_test	= minimize1d(_func, _args, _argc, _min, _max, _eps);
		_fetch_array[i].coord[0]	= min_test.coord;
		_fetch_array[i].value		= min_test.value;
	}
#else
	minimize_fetch1dcl(_func, _fetch_array, _args, _argc, _min, _max, _eps);
#endif
}

#endif